Autor: Paulina Tomaszewska
import numpy as np
import pandas as pd
import sklearn
from sklearn.preprocessing import LabelEncoder
data=pd.read_csv("mushrooms.csv")
data.head()
W poprzedniej pracy domowej wykazano, że dla zbioru danych mushroom zastosowanie target encoding skutkuje niskim wynikiem accuracy. Wynik bliski 100% uzyskano stosując one-hot encoding, która jednak znacząco zwiększa wymiarowość danych. W metodzie SHAP wyliczana jest kontrybucja wszystkich zmiennych, przez co analiza w przypadku dużego większenia liczby kolumn poprzez metodę one-hot jest problematyczna. W ramach dyskusji podczas laboratoriów dowiedziałam się, że zastosowanie metody label encoder (tj. przypisanie kategoriom wartości całkowitych), mimo iż niepoprawne pod względem metodologicznym, prowadzi do wysokiej jakości predycji. Z uwagi na to, że metoda one-hot encoding i label encoding prowadzą do podobnych wartości accuracy, jednak druga z nich nie powoduje zwiększonej liczby zmiennych objaśniających, zdecydowano się na drugą metodę.
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y=le.fit_transform(data['class'])
data.drop('class', axis=1, inplace=True)
le.classes_
dict_encoding={}
for col in data.columns:
data[col]=le.fit_transform(data[col])
dict_encoding[col]=le.classes_
data.head(5)
from sklearn.model_selection import train_test_split
seed = 70
test_size = 0.2
x_train, x_test, y_train, y_test = train_test_split(data, y, test_size=test_size, random_state=seed)
from sklearn.metrics import accuracy_score
from sklearn.svm import SVC
from xgboost import XGBClassifier
from sklearn.ensemble import RandomForestClassifier
import lightgbm as lgb
def classifier(model):
model=model.fit(x_train, y_train)
accuracy=accuracy_score(model.predict(x_test),y_test)
return accuracy*100, model
acc, model_rf =classifier(RandomForestClassifier(max_depth=6, random_state=8))
print("Accuracy RandomForest: %.2f%%" % (acc))
acc, model_xgb=classifier(XGBClassifier(objective ='binary:logistic'))
print("Accuracy XGBoost: %.2f%%" % (acc))
import lightgbm as lgb
# create dataset for lightgbm
d_train = lgb.Dataset(x_train, label=y_train)
d_test = lgb.Dataset(x_test, label=y_test)
#Train the model
params = {
"max_bin": 512,
"learning_rate": 0.005,
"boosting_type": "gbdt",
"objective": "binary",
"metric": "auc",
"num_leaves": 10,
"verbose": -1,
"min_data": 100,
"boost_from_average": True
}
model_lightgbm = lgb.train(params,d_train, 10000, valid_sets=[d_test], early_stopping_rounds=50, verbose_eval=1000)
import shap
import xgboost
def shap_decomposition(model, tree_based, index):
# load JS visualization code to notebook
shap.initjs()
if tree_based==True:
explainer = shap.TreeExplainer(model, model_output='probability', data=shap.sample(x_test,100))
else:
f = lambda x: model.predict_proba(x)[:,1]
explainer=shap.KernelExplainer(model.predict_proba, shap.sample(x_test, 100))
shap_values = explainer.shap_values(x_test.iloc[index,:])
return explainer, shap_values
def shap_extreme(shap_values, index_needed=True, list_index=None):
if index_needed:
data=shap_values[list_index]
else:
data=shap_values
display(pd.DataFrame(data=data, index=x_test.columns).sort_values(by=0, ascending=False).head(5).rename(columns={0: "top shap values"}))
display(pd.DataFrame(data=data, index=x_test.columns).sort_values(by=0, ascending=False).tail(5).rename(columns={0: "smallest shap values"}))
example_index=2
example_sample= x_test.iloc[example_index,:]
model_rf.predict_proba(x_test.values[example_index,:].reshape(1,-1))
explainer
explainer, shap_values=shap_decomposition(model_rf, True, example_index)
# visualize the one prediction's explanation (use matplotlib=True to avoid Javascript)
display(shap.force_plot(explainer.expected_value[1], shap_values[1],example_sample, matplotlib=True))
shap_extreme(shap_values)
print(dict_encoding['gill-size'])
print(dict_encoding['gill-color'])
print(dict_encoding['ring-type'])
dict_encoding['odor']
model_xgb.predict_proba(x_test)[example_index]
explainer, shap_values=shap_decomposition(model_xgb, True, example_index)
shap.initjs()
#plotuje p-stwo klasy 1
display(shap.force_plot(explainer.expected_value, shap_values, x_test.iloc[example_index,:], matplotlib=True))
shap_extreme(shap_values, False)
print(dict_encoding['bruises'][example_sample['bruises']])
print(dict_encoding['stalk-shape'][example_sample['stalk-shape']])
dict_encoding['cap-shape'][example_sample['cap-shape']]
ewentualne problemy z zastosowaniem xgboost: https://evgenypogorelov.com/multiclass-xgb-shap.html
#p-stwo klasy 1
model_lightgbm.predict(x_test)[example_index]
explainer, shap_values=shap_decomposition(model_lightgbm, True,example_index)
shap.initjs()
display(shap.force_plot(explainer.expected_value, shap_values, x_test.iloc[example_index,:], matplotlib=True))
shap_extreme(shap_values,False)
dict_encoding['population'][4]
print(dict_encoding['habitat'][example_sample['habitat']])
print(dict_encoding['cap-shape'][example_sample['cap-shape']])
dict_encoding['stalk-surface-below-ring'][example_sample['stalk-surface-below-ring']]
shap.initjs()
explainer = shap.TreeExplainer(model_lightgbm, model_output='probability', data=shap.sample(x_test,100), matplotlib=True)
shap_values=explainer.shap_values(x_test)
shap.force_plot(explainer.expected_value,shap_values[:1000,:], x_test.iloc[:1000,:])
explainer, shap_values=shap_decomposition(model_lightgbm, True,119)
shap.initjs()
display(shap.force_plot(explainer.expected_value, shap_values, x_train.iloc[119,:], matplotlib=True))
shap_extreme(shap_values,False)
dict_encoding['spore-print-color']
# obserwacja o indeksie 1551
explainer, shap_values=shap_decomposition(model_lightgbm, True,x_test.index==1551)
shap.initjs()
display(shap.force_plot(explainer.expected_value, shap_values, x_test.loc[x_test.index==1551], matplotlib=True))
shap_extreme(shap_values, True, 0)
shap.initjs()
explainer = shap.TreeExplainer(model_lightgbm, model_output='probability', data=shap.sample(x_test,100), matplotlib=True)
shap_values=explainer.shap_values(x_test)
shap.summary_plot(shap_values, x_test)
shap.initjs()
for name in x_test.columns:
shap.dependence_plot(name, shap_values, x_test, display_features=x_test)
from sklearn.linear_model import LogisticRegression
acc, model_logistic=classifier(SVC(probability=True))
print(acc)
explainer, shap_values=shap_decomposition(model_logistic, False, example_index)
shap.initjs()
display(shap.force_plot(explainer.expected_value[1], shap_values[1], example_sample, matplotlib=True))
shap_extreme(shap_values, True)
explainer, shap_values=shap_decomposition(model_logistic, False, 119)
shap.initjs()
display(shap.force_plot(explainer.expected_value[1], shap_values[1], x_test.iloc[119,:], matplotlib=True))
shap_extreme(shap_values, True)
dict_encoding['population'][2]
Narzędzie SHAP pozwala na tworzenie ciekawych analiz. Analiza dekompozycji dla tej samej zmiennej jednak gdy użyto inne klasyfikatory (choć o bardzo podobnym accuracy) ujawniła odmienne kontrybucje zmiennych, jednak zawsze wśród trzech najważniejszych zmiennych znajdowała się gill-size lub odor. Jednak pozostałe dwie najważniejsze zmienne się różniły i udało się zademostrować taki przykład. Zaobserwowano, że ta sama zmienna w zależności od analizowanej obserwacji może zwiększać bądź zmniejszać predykcję.